/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package lifecycle;

import static autodispose.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static autodispose.harmony.MainThreadDisposable.isMainThread;
import static ohos.aafwk.ability.Lifecycle.Event.ON_ACTIVE;
import static ohos.aafwk.ability.Lifecycle.Event.ON_FOREGROUND;
import static ohos.aafwk.ability.Lifecycle.Event.ON_START;
import static ohos.aafwk.ability.Lifecycle.Event.ON_STOP;

import autodispose.annotation.RestrictTo;
import autodispose.harmony.MainThreadDisposable;
import io.reactivex.rxjava3.annotations.Nullable;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
import ohos.aafwk.ability.Lifecycle;
import ohos.aafwk.ability.LifecycleStateObserver;
import ohos.aafwk.content.Intent;

/**
 * This is an observer class.
 *
 * @author Rohit rwx1000605
 */
@RestrictTo(LIBRARY_GROUP)
class LifecycleEventsObservable extends Observable<Lifecycle.Event> {
    private static final String TAG_LOG = LifecycleEventsObservable.class.getName();

    private final Lifecycle lifecycle;

    private final BehaviorSubject<Lifecycle.Event> eventsObservable = BehaviorSubject.create();

    LifecycleEventsObservable(Lifecycle lifecycle) {
        this.lifecycle = lifecycle;
    }

    Lifecycle.Event getValue() {
        return eventsObservable.getValue();
    }

    /**
     * Backfill if already created for boundary checking. We do trick here for corresponding events
     * where we pretend something is created upon initialized state so that it assumes the
     * corresponding event is DESTROY.
     */
    void backfillEvents() {
        @Nullable Lifecycle.Event correspondingEvent;

        switch (lifecycle.getLifecycleState()) {
            case UNDEFINED:
                correspondingEvent = ON_START;
                break;
            case ON_START:
                correspondingEvent = ON_ACTIVE;
                break;
            case ON_ACTIVE:
                correspondingEvent = ON_ACTIVE;
                break;
            case ON_FOREGROUND:
                correspondingEvent = ON_FOREGROUND;
                break;
            case ON_STOP:
                correspondingEvent = ON_STOP;
                break;
            default:
                correspondingEvent = ON_STOP;
                break;
        }
        eventsObservable.onNext(correspondingEvent);
    }

    @Override
    protected void subscribeActual(Observer<? super Lifecycle.Event> observer) {
        AutoDisposeLifecycleObserver lifecycleObserver = new AutoDisposeLifecycleObserver(lifecycle, observer,
            eventsObservable);
        observer.onSubscribe(lifecycleObserver);
        if (!isMainThread()) {
            observer.onError(new IllegalStateException("Lifecycles can only be bound to on the main thread!"));
            return;
        }
        lifecycle.addObserver(lifecycleObserver);
        if (lifecycleObserver.isDisposed()) {
            lifecycle.removeObserver(lifecycleObserver);
        }
    }

    /**
     * This is lifecycle observer.
     */
    static final class AutoDisposeLifecycleObserver extends MainThreadDisposable implements LifecycleStateObserver {
        private final Lifecycle lifecycle;

        private final Observer<? super Lifecycle.Event> observer;

        private final BehaviorSubject<Lifecycle.Event> eventsObservable;

        AutoDisposeLifecycleObserver(Lifecycle lifecycle, Observer<? super Lifecycle.Event> observer,
            BehaviorSubject<Lifecycle.Event> eventsObservable) {
            this.lifecycle = lifecycle;
            this.observer = observer;
            this.eventsObservable = eventsObservable;
        }

        @Override
        protected void onDispose() {
            lifecycle.removeObserver(this);
        }

        @Override
        public void onStateChanged(Lifecycle.Event event, Intent intent) {
            if (!isDisposed()) {
                if (!(eventsObservable.getValue() == event)) {
                    eventsObservable.onNext(event);
                }
                observer.onNext(event);
            }
        }
    }
}
